import { App, Directive, Plugin } from "vue"
import { matrix, multiply, flatten, identity, Matrix } from 'mathjs'

// 小孔成像纠正 矩阵
const pinhole = matrix([
  [-1, 0, 0, 0],
  [0, -1, 0, 0],
  [0, 0, 1, 0],
  [0, 0, 0, 1]
])

function distance(...arr) {
  return Math.sqrt(arr.reduce((t,v) => t+=v**2, 0))
}


function transform(e: MouseEvent | HTMLElement, boo?: boolean) {
  let ev: MouseEvent
  let el: HTMLElement
  
  if ((e as MouseEvent).currentTarget) {
    ev = e as MouseEvent
    el = ev.currentTarget as HTMLElement
  } else {
    el = e as HTMLElement
  }
  
  let offsetX = 0
  let offsetY = 0
  
  if (ev != null) {
    const rect = el.getBoundingClientRect()
    offsetX = ev.clientX - rect.left
    offsetY = ev.clientY - rect.top
  }

  let w = el.offsetWidth, w2 = w / 2
  let h = el.offsetHeight, h2 = h / 2

  
  const radius = distance(w2, h2)
  const ratio = distance(offsetX - w2, offsetY - h2) / radius
  let rotateX = -2 * (offsetY-h2)/h2, rotateY = 2 * (offsetX-w2)/w2
  let thetX = rotateX * (Math.PI/180), thetY = rotateY * (Math.PI/180)

  
  // y旋转矩阵
  const rotationYT = matrix([
    [Math.cos(thetY), 0, -Math.sin(thetY), 0],
    [0, 1, 0, 0],
    [Math.sin(thetY), 0, Math.cos(thetY), 0],
    [0, 0, 0, 1]
  ])
  // x旋转矩阵
  const rotationXT = matrix([
    [1, 0, 0, 0],
    [0, Math.cos(thetX), Math.sin(thetX), 0],
    [0, -Math.sin(thetX), Math.cos(thetX), 0],
    [0, 0, 0, 1]
  ])

  // 矩阵变换的原点
  const ox = w2, oy = h2, oz = boo ?  w2 + radius*0.02 * (1-ratio) : w2
  const transOT = matrix([
    [1, 0, 0, 0],
    [0, 1, 0, 0],
    [0, 0, 1, 0],
    [-ox, -oy, -oz, 1]
  ])
  const transCenterT = matrix([
    [1, 0, 0, 0],
    [0, 1, 0, 0],
    [0, 0, 1, 0],
    [ox, oy, oz, 1]
  ])

  let n = -w / 2
  let f = -w * 1000

  let r = 1
  let l = -1
  let t = 1
  let b = -1

  let m00 = 2 * n / (r - l)
  let m11 = 2 * n / (t - b)
  let m22 = (n + f) / (n - f)
  let m23 = 2 * f * n / (n - f)
  // 透视矩阵
  const aperspectT = matrix([
    [m00, 0, 0, 0],
    [0, m11, 0, 0],
    [0, 0, m22, -1],
    [0, 0, m23, 0]
  ])

  let result = identity(4) as Matrix
  
  if (boo) {
    const ox = w-offsetX, oy = h-offsetY, oz = 0
    const transOT = matrix([
      [1, 0, 0, 0],
      [0, 1, 0, 0],
      [0, 0, 1, 0],
      [-ox, -oy, -oz, 1]
    ])
    const transCenterT = matrix([
      [1, 0, 0, 0],
      [0, 1, 0, 0],
      [0, 0, 1, 0],
      [ox, oy, oz, 1]
    ])
    result = multiply(result, transOT)
    //
    result = multiply(result, rotationYT) // x 旋转
    result = multiply(result, rotationXT) // y 旋转
    //
    result = multiply(result, transCenterT)
  }

  //把图像移动至原点(0, 0),方便计算
  result = multiply(result, transOT)

  //透视
  result = multiply(result, aperspectT)
  //小孔成像纠正
  result = multiply(result, pinhole)

  //还原至画布中央
  result = multiply(result, transCenterT)

  // @ts-ignore
  let mStr = flatten(result)._data.join(',')
  el.style.transform = `matrix3d(${mStr}) `
  el.style.transformOrigin = `0 0`
}

function addEventListener(el: HTMLElement, mousedown: (e: MouseEvent) => void, mouseup: (e: MouseEvent) => void) {
  const _up = e => {
    mouseup(e)
    window.removeEventListener('mouseup', _up)
  }
  const _down = e => {
    mousedown(e)
    window.addEventListener('mouseup', _up)
  }
  el.addEventListener('mousedown', _down)
}



const name = 'sunken'

const Sunken: Directive & Partial<Plugin> & { name: string } = {
  name,
  mounted(el: HTMLElement) {
    el.style.transition = `unset`
    transform(el)
    void el.offsetWidth
    el.style.transition = `transform .1s`

    addEventListener(el, e => {
      transform(e, true)
    }, e => {
      transform(el)
    })
  }
}

Sunken.install = (app: App) => {
  app.directive(Sunken.name, Sunken)
}

export default Sunken